//package com.qfoud.edu.framwork.servlet;
//
//import com.alibaba.fastjson.JSON;
//import com.qfoud.edu.framwork.adviser.RequestAndResponseAdviser;
//import com.qfoud.edu.framwork.annotation.EnableGlobleExeption;
//import com.qfoud.edu.framwork.annotation.EnableRequestAndResponseAdviser;
//import com.qfoud.edu.framwork.container.MappingContainer;
//import com.qfoud.edu.framwork.exception.HandlerExceptionResolve;
//import com.qfoud.edu.framwork.exception.MvcFramworkException;
//import com.qfoud.edu.framwork.interceptor.base.InterceptorChain;
//import com.qfoud.edu.framwork.interceptor.impl.InterceptorChainImpl;
//import com.qfoud.edu.framwork.pojo.*;
//import com.qfoud.edu.framwork.utils.JavaTypeUtils;
//import com.qfoud.edu.framwork.utils.StringUtils;
//import org.apache.commons.beanutils.BeanUtils;
//
//import javax.servlet.ServletConfig;
//import javax.servlet.ServletContext;
//import javax.servlet.ServletException;
//import javax.servlet.ServletInputStream;
//import javax.servlet.http.HttpServlet;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.IOException;
//import java.io.PrintWriter;
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//import java.util.Iterator;
//import java.util.List;
//import java.util.Map;
//
///**
// * @author xiaobobo
// * @title: DispatcherServlet
// * @projectName cd-java-2303-project-parent
// * @description: 这个里面要干的事情：
// *     1、取出请求的URI地址
// *     2、在我们的容器中去找beandefinition对象
// *     3、取出method
// *     4、采用method.invoke()方法去执行
// * @date 2023/5/8  11:01
// */
//public class DispatcherServletBack extends HttpServlet {
//
//    //这个是全局异常的模板
//    private HandlerExceptionResolve handlerExceptionResolve=null;
//
//    //这个是前后置处理器的实例对象(多态)
//    private RequestAndResponseAdviser requestAndResponseAdviser=null;
//
//    //这里维护一个实例
//    private InterceptorChain interceptorChain=InterceptorChainImpl.getInstance();
//
//    @Override
//    public void init(ServletConfig config) throws ServletException {
//        super.init(config);
//        //接下来在这里进行初始化操作
//        //这个方法是用来初始化我们的全局异常的实例的
//        initHandlerExceptionResolve(config);
//        //这里是实例化我们的这个前置和后置处理器的地方
//        initRequestAndResponseAdviser(config);
//    }
//
//    /**
//     * 这里是实例化我们的前置和后置处理器的地方
//     * @param config
//     */
//    private void initRequestAndResponseAdviser(ServletConfig config) {
//        //获取上下文的对象
//        ServletContext servletContext = config.getServletContext();
//        //找Config的class对象
//        Class<?> configClass = getConfigClass(servletContext);
//        EnableRequestAndResponseAdviser annotation = configClass.getAnnotation(EnableRequestAndResponseAdviser.class);
//        if(null!=annotation){
//            //说明程序员是设置了这个处理器的
//            String value = annotation.value();
//            if(StringUtils.isEmpty(value)){
//               throw new MvcFramworkException("前后置处理器的处理类未设置....");
//            }
//            //程序执行到这里：说明设置了处理类
//            Class<?> aClass=null;
//            try {
//                aClass = Class.forName(value);
//            } catch (ClassNotFoundException e) {
//                throw new MvcFramworkException("前后置处理器的处理类路径设置有误....");
//            }
//            //程序执行到这里说明这个类路径无误
//            try {
//                requestAndResponseAdviser = (RequestAndResponseAdviser) aClass.newInstance();
//            } catch (Exception e) {
//                throw new MvcFramworkException("前后置处理器的实例化出错....");
//            }
//        }
//    }
//
//    /**
//     * 初始化全局异常模板
//     * @param config
//     */
//    private void initHandlerExceptionResolve(ServletConfig config) {
//        //获取上下文的对象
//        ServletContext servletContext = config.getServletContext();
//        //找Config的class对象
//        Class<?> configClass = getConfigClass(servletContext);
//        //接下来就可以判断是否有EnableGlobException这个注解
//        EnableGlobleExeption enableGlobleExeption = configClass.getAnnotation(EnableGlobleExeption.class);
//        if(null!=enableGlobleExeption){
//            //说明有这样一个注解
//            //获取值(全局异常实现类的全路径)
//            String globleExceptionPath = enableGlobleExeption.value();
//            if(StringUtils.isEmpty(globleExceptionPath)){
//                throw new MvcFramworkException("全局异常的类路径未设置");
//            }
//            //程序执行到这里说明是对的
//            Class<?> globleExceptionClass=null;
//            try {
//                globleExceptionClass = Class.forName(globleExceptionPath);
//            } catch (ClassNotFoundException e) {
//                e.printStackTrace();
//                throw new MvcFramworkException("全局异常类路径反射出错");
//            }
//            //程序执行到这里：说明反射无误
//            try {
//                handlerExceptionResolve = (HandlerExceptionResolve) globleExceptionClass.newInstance();
//            } catch (Exception e) {
//                e.printStackTrace();
//                throw new MvcFramworkException("全局异常程序实例化出错");
//            }
//
//
//        }
//    }
//
//    /**
//     * 找配置文件的class对象
//     * @param servletContext
//     * @return
//     */
//    private Class<?> getConfigClass(ServletContext servletContext) {
//        //第一步：找到配置文件
//        String config = servletContext.getInitParameter("config");
//        if(StringUtils.isEmpty(config)){
//            throw new MvcFramworkException("web.xml中未配置config节点");
//        }
//        //程序执行到这里说明配置了节点
//        //第二步：反射获取这个类的class对象
//        Class<?> configClass=null;
//        try {
//            configClass = Class.forName(config);
//        } catch (ClassNotFoundException e) {
//            throw new MvcFramworkException("config中的类路径有误");
//        }
//        return configClass;
//    }
//
//
//    @Override
//    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        //取出请求的URI地址  端口之后  ？ 号之前的那一节地址
//        String requestURI = req.getRequestURI();
//        //设置一下这个编码
//        req.setCharacterEncoding("UTF-8");
//        //在容器中去找 这个beandefinition对象
//        BeanDefinition beanDefinition = MappingContainer.getMappingContainer().get(requestURI);
//        //判断这个beandefinition对象是否为空
//        if(null==beanDefinition){
//            throw new MvcFramworkException("no mapping");
//        }
//        //接下来执行到这里：说明beandefinition不为空
//        //取出这个类的实例信息
//        Object instance = beanDefinition.getInstance();
//        //取出method
//        Method method = beanDefinition.getBeanMethodDefinition().getMethod();
//        //接下来开始通过反射执行
//        try {
//            //这里去触发这个拦截器的调用
//            interceptorChain.dealReq(req);
//            //这里申明一个用来装参数的这样一个数组
//            Object[] args=new Object[beanDefinition.getBeanMethodDefinition().getBeanParameterDefinitions().size()];
//            //申明Model
//            Model model = new Model();
//            handlerParameters(req,beanDefinition,resp,model,args);
//            Object invoke = method.invoke(instance, args);
//            //接下来要处理返回的结果
//            handlerReturnVal(req,resp,invoke,model,beanDefinition);
//        } catch (Exception e) {
//           // e.printStackTrace();
//            System.out.println("反射执行目标方法出错....");
//            if(null!=handlerExceptionResolve){
//                //说明程序员配置了全局异常
//                handlerExceptionResolve.handler(e.getCause(),req,resp);
//            }
//        }
//    }
//
//    /**
//     * 处理参数
//     * @param req
//     * @param beanDefinition
//     */
//    private void handlerParameters(HttpServletRequest req, BeanDefinition beanDefinition,HttpServletResponse resp,Model model,Object...args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//        //首先第一步：我们要获取参数集合
//        List<BeanParameterDefinition> beanParameterDefinitions = beanDefinition.getBeanMethodDefinition().getBeanParameterDefinitions();
//        // 如果集合是ArrayList的话 这个集合是实现了 RandomAccess这个接口的
//        // RandomAccess 支持随机快速的访问  这个随机快速的访问 在fori的情况下是效率很高的
//        // for each的底层是迭代器 迭代器本身在 LinkedList的效率比较高 所以这里最好使用 fori
//
//        for (int i = 0; i <beanParameterDefinitions.size() ; i++) {
//            BeanParameterDefinition beanParameterDefinition = beanParameterDefinitions.get(i);
//            //接下来就要处理单个的参数了...
//            handlerSingleParameter(beanParameterDefinition,req,i,resp,model,args);
//        }
//    }
//    /**
//     * 处理单个参数
//     * @param beanParameterDefinition
//     * @param req
//     */
//    private void handlerSingleParameter(BeanParameterDefinition beanParameterDefinition, HttpServletRequest req,int index,HttpServletResponse resp,Model model,Object... args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, IOException {
//        //首先要获取参数的类型
//        // Java中的单个数据类型
//        // HttpServlerRequest
//        // HttpServletResponse
//        // 自定义类的类型
//        // JSON格式转换的算一类
//        //第一步：获取类型
//        String parameterType = beanParameterDefinition.getParameterType();
//        System.out.println("参数类型是:"+parameterType);
//        String parameterName = beanParameterDefinition.getParameterName();
//        //判断是不是Java中的单个数据类型
//        //TODO 这里直接使用策略的设计模式去进行优化
//        if(jugmentTypeIsJavaOrNot(parameterType)){
//            //这里就是处理Java类型的参数设置
//            javaTypeParameterSet(req, index, parameterType, parameterName, args);
//        }else if(StringUtils.equals(parameterType,"javax.servlet.http.HttpServletRequest")){
//            //说明需要的参数是request
//            args[index]=req;
//        }else if(StringUtils.equals(parameterType,"javax.servlet.http.HttpServletResponse")){
//            //说明需要的参数是response
//            args[index]=resp;
//        }else if(StringUtils.equals(parameterType, "com.qfoud.edu.framwork.pojo.Model")){
//            //说明传递了一个Model值
//            args[index]=model;
//        }else if(StringUtils.equals("java.lang.String[]",parameterType)){
//            //说明需要的是一个String类型的数组来接受参数的
//            stringArrayTypeParameterSet(req, index, parameterType, parameterName, args);
//        }else{
//            //如果不是Java类型的参数 那么就是对象类型
//            objectTypeParameterSet(beanParameterDefinition,req,index,parameterType,parameterName,args);
//        }
//
//    }
//
//    /**
//     * 这个说明需要将前端的参数封装成String类型的数组
//     * @param req
//     * @param index
//     * @param parameterType
//     * @param parameterName
//     * @param args
//     */
//    private void stringArrayTypeParameterSet(HttpServletRequest req, int index,
//                                             String parameterType, String parameterName, Object[] args) {
//        String[] parameterValues = req.getParameterValues(parameterName);
//        args[index]=parameterValues;
//    }
//
//    /**
//     * 对象类型的参数设置
//     * @param req
//     * @param index
//     * @param parameterType
//     * @param parameterName
//     * @param args
//     */
//    private void objectTypeParameterSet(BeanParameterDefinition beanParameterDefinition,HttpServletRequest req, int index, String parameterType, String parameterName, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {
//
//        //这里有两种情况
//        // 第一种情况：前端传JSON ->Java
//        // 第二种情况：前端传键值对->Java对象
//        Boolean requestBodyOrNot = beanParameterDefinition.getRequestBodyOrNot();
//        if(requestBodyOrNot){
//            //说明前端传递的是JSON格式
//            // 注意：HTTP协议传递JSON的时候 数据只能放到 请求实体中 请求实体中的数据: 流
//            // 复习:get -->getParameter()   getQueryString()
//            // 复习:post --> 键值对的时候 getParameter()   getInputStream();
//            String str=getReqData(req);
//            //TODO 触发点原来在这里(requestBody之前可以解密)
//            if(null!=requestAndResponseAdviser){
//                str=requestAndResponseAdviser.before(str);
//            }
//            //将这个类型弄成class对象
//            Class<?> aClass = Class.forName(parameterType);
//            //接下来就要将这个值转换成对象？
//            Object object = JSON.parseObject(str, aClass);
//            args[index]=object;
//            return ;
//        }
//        //第一步：获取这个类型的class对象
//        Class<?> clazz = Class.forName(parameterType);
//        //实例化对象
//        Object object = clazz.newInstance();
//        //要将req中的参数设置给 args
//        BeanUtils.populate(object,req.getParameterMap());
//        //将这个对象设置给参数
//        args[index]=object;
//    }
//
//    /**
//     * 获取请求数据(从请求中获取)
//     * @param req
//     * @return
//     */
//    private String getReqData(HttpServletRequest req) throws IOException {
//        ServletInputStream in = req.getInputStream();
//        byte[] buf=new byte[8192];
//        int readLength = in.read(buf);
//        //将获取的数据进行转换
//        return new String(buf,0,readLength,"UTF-8");
//    }
//
//    /**
//     * Java类型的参数设置
//     * @param req
//     * @param index
//     * @param parameterType
//     * @param parameterName
//     * @param args
//     */
//    private void javaTypeParameterSet(HttpServletRequest req, int index, String parameterType, String parameterName, Object[] args) {
//        //如果是Java中的单个的数据类型的话 那么执行下面的函数
//        String parameter = req.getParameter(parameterName);
//        //获取当前的数据类型
//        switch (parameterType){
//            case "java.lang.Integer":
//            case "int":
//                int val=Integer.parseInt(parameter);
//                args[index]=val;
//                break;
//            case "java.lang.String":
//            case "String":
//                args[index]=parameter;
//                break;
//
//            case "java.lang.Float":
//            case "float":
//                float floatVal = Float.parseFloat(parameter);
//                args[index]=floatVal;
//                break;
//
//            case "java.lang.Boolean":
//            case "boolean":
//                boolean b = Boolean.parseBoolean(parameter);
//                args[index]=b;
//                break;
//
//            case "java.lang.Double":
//            case "double":
//                double v = Double.parseDouble(parameter);
//                args[index]=v;
//                break;
//        }
//    }
//
//    /**
//     * 判断数据类型是不是Java中的单个数据类型
//     * @param parameterType
//     * @return
//     */
//    private boolean jugmentTypeIsJavaOrNot(String parameterType) {
//        String[] javaType = JavaTypeUtils.getJavaType();
//        for (int i = 0; i <javaType.length ; i++) {
//            String type=javaType[i];
//            if(StringUtils.equals(type,parameterType)){
//               return true;
//            }
//        }
//        return false;
//    }
//
//    /**
//     * 处理返回结果
//     * @param req
//     * @param resp
//     * @param invoke
//     */
//    private void handlerReturnVal(HttpServletRequest req, HttpServletResponse resp, Object invoke,Model model,BeanDefinition beanDefinition) throws IOException, ServletException {
//        //说明返回的是其他的数据
//        //获取方法的描述
//        BeanMethodDefinition beanMethodDefinition = beanDefinition.getBeanMethodDefinition();
//        Boolean responseBodyOrNot = beanMethodDefinition.getResponseBodyOrNot();
//        if(responseBodyOrNot){
//            //执行到这里：说明这个鬼需要转换成JSON格式将数据写回去
//            sendResponse(resp,invoke);
//            return;
//        }else if(invoke instanceof String){
//            handlerReturnStringType(req, resp, invoke,model);
//            return;
//        }else if(invoke instanceof ModelAndView){
//            //说明返回了一个ModelAndView对象
//            handleModelAndViewType(req, resp, invoke);
//            return ;
//        }else{
//            if(!responseBodyOrNot){
//                throw new MvcFramworkException("非法的返回值");
//            }
//        }
//    }
//
//    /**
//     * 将数据以JSON的格式直接写回去
//     * @param resp
//     * @param invoke
//     */
//    private void sendResponse(HttpServletResponse resp, Object invoke) {
//        //第一步：将invoke转换成JSON格式
//        String jsonString = JSON.toJSONString(invoke);
//        //TODO：前置和后置处理器的触发点
//        if(null!=requestAndResponseAdviser){
//            jsonString= requestAndResponseAdviser.after(jsonString);
//        }
//        //第二步：将数据写回到我们的页面中
//        resp.setContentType("text/html;charset=utf-8");
//        PrintWriter writer = null;
//        try {
//            writer = resp.getWriter();
//            writer.write(jsonString);
//            writer.flush();
//            writer.close();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
//
//    /**
//     * 处理返回ModelAndView对象的
//     * @param req
//     * @param resp
//     * @param invoke
//     */
//    private void handleModelAndViewType(HttpServletRequest req, HttpServletResponse resp, Object invoke) throws ServletException, IOException {
//        //先将返回值弄成ModelAndView这个值
//        ModelAndView modelAndView= (ModelAndView) invoke;
//        //将值取出来
//        setDataToReq(req,modelAndView);
//        //接下来取出这个要跳转的页面
//        String viewName = modelAndView.getViewName();
//        //TODO 跳转页面
//        jumpPage(viewName,req,resp);
//    }
//
//    /**
//     * 处理咋们的String类型的返回
//     * @param req
//     * @param resp
//     * @param invoke
//     * @throws IOException
//     * @throws ServletException
//     */
//    private void handlerReturnStringType(HttpServletRequest req, HttpServletResponse resp, Object invoke,Model model) throws IOException, ServletException {
//        //说明需要进行页面的跳转
//        String path= String.valueOf(invoke);
//        //接下来判断这个地址是否以 redirect:
//        if(path.startsWith("redirect:")){
//            //这里还需要将地址前面的redirect给去掉
//            path=path.replace("redirect:","").trim();
//            //如果成立说明是重定向
//            resp.sendRedirect(path);
//            return ;
//        }
//        //执行到这里才需要考虑这个Model中数据的问题 因为是转发
//        if(model.getMaps().size()>0){
//            //说明有数据放到了域对象中
//            //接下来就要将数据取出来 最终放到req中去
//            setDataToReq(req,model);
//        }
//        jumpPage(path,req,resp);
//        return ;
//    }
//
//    /**
//     * 页面跳转
//     * @param path
//     * @param req
//     * @param resp
//     */
//    private void jumpPage(String path, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        //执行到这里说明是转发
//        req.getRequestDispatcher(path).forward(req,resp);
//    }
//
//    /**
//     * 这里就是将Model中的数据放到req中去
//     * @param req
//     * @param model
//     */
//    private void setDataToReq(HttpServletRequest req, Model model) {
//        //获取迭代器
//        Iterator<Map.Entry<String, Object>> it = model.getMaps().entrySet().iterator();
//        while (it.hasNext()){
//            Map.Entry<String, Object> next = it.next();
//            //下面获取放到Model中的键值对
//            String key = next.getKey();
//            Object value = next.getValue();
//            req.setAttribute(key,value);
//        }
//
//    }
//}
